package com.galaxy.commom.base.filter;

import com.galaxy.commom.base.utils.Collections3;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * 防XSS漏洞Request包装器
 * @author caijinbang
 * @date 2018/9/8 22:55
 */
public class XssHttpRequestWrapper extends HttpServletRequestWrapper {

  private static Pattern scriptPattern_between = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
  private static Pattern scriptPattern_src = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  private static Pattern scriptPattern_src_2 = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  private static Pattern scriptPattern_end = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
  private static Pattern scriptPattern_begin = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  private static Pattern scriptPattern_eval = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  private static Pattern scriptPattern_expression = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  private static Pattern scriptPattern_javascript = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
  private static Pattern scriptPattern_vbscript = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
  private static Pattern scriptPattern_onload = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);

  private HttpServletRequest orgRequest = null;

  /**
   * 构造函数
   * @param request 请求
   */
  public XssHttpRequestWrapper(HttpServletRequest request) {
    super(request);
    this.orgRequest = request;
  }

  /**
   * 获取请求头
   * @param name 请求头名字
   * @return 请求头值
   */
  @Override
  public String getHeader(String name) {
    String value = super.getHeader(name);
    if (value != null) {
      value = this.xssEncode(value);
    }
    return value;
  }

  /**
   * 根据名称获取参数
   * @param name 参数名称
   * @return 参数值
   */
  @Override
  public String getParameter(String name) {
    String value = super.getParameter(name);
    if (value != null) {
      value = this.xssEncode(value);
    }
    return value;
  }

  /**
   * 根据参数获取参数集合
   * @param name 参数名称
   * @return 参数集合
   */
  @Override
  public String[] getParameterValues(String name) {
    String[] values = super.getParameterValues(name);
    List<String> strings = Arrays.asList(values);
    if (Collections3.isNotEmpty(strings)) {
      for (int i = 0; i < values.length; i++) {
        values[i] = this.xssEncode(values[i]);
      }
    }
    return values;
  }

  /**
   * 将可能引起xss漏洞的半角字符直接替换成全角字符
   * @param value 漏洞字符串
   * @return 全角字符串
   */
  private String xssEncode(String value) {
    if (value == null || value.isEmpty()) {
      return value;
    }
    value = stripXSS(value);
    StringBuilder sb = new StringBuilder(value.length() + 16);
    for (int i = 0; i < value.length(); i++) {
      char c = value.charAt(i);
      switch (c) {
        case '>':
          // 全角大于号
          sb.append('＞');
          break;
        case '<':
          // 全角小于号
          sb.append('＜');
          break;
        case '\'':
          // 全角单引号
          sb.append('‘');
          break;
        case '\"':
          // 全角双引号
          sb.append('“');
          break;
        case '&':
          // 全角
          sb.append('＆');
          break;
        case '\\':
          // 全角斜线
          sb.append('＼');
          break;
        case '#':
          // 全角井号
          sb.append('＃');
          break;
        case '(':
          sb.append('（');
          break;
        case ')':
          sb.append('）');
          break;
        default:
          sb.append(c);
          break;
      }
    }
    return sb.toString();
  }

  /**
   * 将可能产生XSS漏洞的字符串替换空串
   * @param value 漏洞字符串
   * @return 处理后的字符串
   */
  private String stripXSS(String value) {
    if (value != null) {
      value = scriptPattern_between.matcher(value).replaceAll("");
      value = scriptPattern_src.matcher(value).replaceAll("");
      value = scriptPattern_src_2.matcher(value).replaceAll("");
      value = scriptPattern_end.matcher(value).replaceAll("");
      value = scriptPattern_begin.matcher(value).replaceAll("");
      value = scriptPattern_eval.matcher(value).replaceAll("");
      value = scriptPattern_expression.matcher(value).replaceAll("");
      value = scriptPattern_javascript.matcher(value).replaceAll("");
      value = scriptPattern_vbscript.matcher(value).replaceAll("");
      value = scriptPattern_onload.matcher(value).replaceAll("");
    }
    return value;
  }
}
